#include "stdafx.h"
#include "../lib/lib.h"
#include "config.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include "license.h"
#include "url_query.h"
#include <sys/ioctl.h>
#include <netdb.h>

#define MAX_EVENT		10

/* 请求的URL链表 */
struct request_chain
{
	struct request_chain*		next;
	unsigned int				timeout;
	unsigned int				hash;
	struct sockaddr_in			addr;
};
/* 远程请求上下文信息 */
struct tcp_context
{
	int							fd;
	struct sockaddr_in			addr;
	char						url[257];
};

static int				fd_sock				= -1; // 监听的SOCKET
static int				fd_epoll			= -1; // epoll
struct request_chain*	root_req_chain		= NULL; // 请求的socket链

void	myexit(void);
void	start_listen(void);
void	do_udp_request(void);
void	do_tcp_request(int events, struct tcp_context* ptcp);
void	udp_remote_check(struct sockaddr_in* paddr, const char* purl);
void	tcp_remote_check(struct sockaddr_in* paddr, const char* purl);
void	sendto_remote(struct sockaddr_in* paddr, const char* purl
			, uint8_t service, uint8_t tp1, uint8_t tp2
			, uint8_t tp3, uint8_t rating);
void	update_url_lib(qut_pack_t* ppack);
void	response_clients(const char* purl, uint8_t tp1, uint8_t tp2
			, uint8_t tp3, uint8_t rating);
void	tcp_send_get(struct tcp_context* ptcp);

// 主函数
int main(void)
{
	struct sockaddr_in		addr	= {0};
	struct epoll_event		event	= {0};

	// 初始化配置
	atexit(myexit);
	ini_init();
	url_init();
	openlog("[quts]", LOG_PID, LOG_USER);

	fd_sock = socket(AF_INET, SOCK_DGRAM, 0);
	addr.sin_family = AF_INET;
	addr.sin_port = htons(ini_get_listen_port());
	addr.sin_addr.s_addr = htonl(0/*INADDR_ANY*/);

	// 绑定端口
	if( bind(fd_sock, (struct sockaddr*)&addr, sizeof(addr)) )
	{
		syslog(LOG_ERR, "bind %d port failed: %s"
				, ini_get_listen_port(), strerror(errno));
		return 0;
	}
	syslog(LOG_NOTICE, "start listen.");
	
	// 创建epoll
	fd_epoll = epoll_create(MAX_EVENT);
	if( -1 == fd_epoll )
	{
		syslog(LOG_ERR, "epoll_create error: %s", strerror(errno));
		return 0;
	}

	event.events = EPOLLIN;
	event.data.fd = fd_sock;
	if( epoll_ctl(fd_epoll, EPOLL_CTL_ADD, fd_sock, &event) )
	{
		syslog(LOG_ERR, "epoll_ctl server sock error: %s"
				, strerror(errno));
		return 0;
	}
	// 开始接收数据
	start_listen();

	return 0;
}

/*
 * 退出清空操作
 */
void myexit(void)
{
	// 关闭日志
	closelog();
	// 关闭套接字
	if( -1 != fd_sock )
	{
		close(fd_sock);
	}
	// 关闭epoll
	if( -1 != fd_epoll )
	{
		close(fd_epoll);
	}
}

/*
 * 开始监听
 */
void start_listen()
{
	struct epoll_event		events[MAX_EVENT]	= {0};
	int						count				= 0;
	int						i;
	int						fd;

	while(true)
	{
		bzero(events, sizeof(events));
		// 等待连接
		count = epoll_wait(fd_epoll, events, MAX_EVENT, -1);
		if( -1 == count && EINTR != errno)
		{
			syslog(LOG_ERR, "epoll_wait: %s", strerror(errno));
			return;
		}
		// 处理接收
		for(i = 0; i < count; i++)
		{
			if(events[i].data.fd == fd_sock)
			{
				do_udp_request();
			}
			else if( events[i].events & (EPOLLIN | EPOLLOUT) )
			{
				do_tcp_request(events[i].events, events[i].data.ptr);
			}
			else if(NULL != events[i].data.ptr)
			{
				// 发生错语时， 把文件描述符去除
				fd = ((struct tcp_context *)events[i].data.ptr)->fd;
				epoll_ctl(fd_epoll, EPOLL_CTL_DEL, fd, &events[i]);
				close(fd);
				free(events[i].data.ptr);
			}
		}
	} // while(true)
}
/*
 * 处理UDP请求
 */
void do_udp_request()
{
	char					data[1024]		= {0};
	struct sockaddr_in		addr			= {0};
	int						len				= sizeof(addr);
	qut_pack_t				*ppack			= (qut_pack_t *)data;
	uint32_t				size			= 0;
	uint8_t					tp1 = 0, tp2 = 0, tp3 = 0, rating = 0;

	// 接收数据
	if( -1 == recvfrom(fd_sock, data, sizeof(data), 0
				, (struct sockaddr*)&addr, &len) )
	{
		syslog(LOG_ERR, "recvfrom: %s", strerror(errno));
		exit(0);
	}
	// 处理数据
	if( 2 != ppack->version )
	{
		// 版本不对不与处理
		return;
	}
	size = ntohs(ppack->des_size);
	if(272 < size)
	{
		// 数据区的大小不对
		return;
	}
	// 解密数据
	if( false == myencrypt(data + 4, &size, false) )
	{
		return; // 解密数据错误
	}
	// 判断服务类型
	ppack->url[ppack->url_size] = 0;
	if( 0 != ppack->service )
	{
		update_url_lib(ppack);
		return;
	}
	// 验证授权
	if( false == lcn_check(ppack->head.lcn) )
	{
		// 授权信息不对
		return;
	}
	// 验证URL
	if( false == url_query(ppack->url, &tp1, &tp2, &tp3, &rating) )
	{
		if( 0 == ini_get_server_type() )
		{
			udp_remote_check(&addr, ppack->url);
		}
		else
		{
			tcp_remote_check(&addr, ppack->url);
		}
		return;
	}
	// 回应客户端
	sendto_remote(&addr, ppack->url, 1, tp1, tp2, tp3, rating);
}
/*
 * 更新URL库
 */
void update_url_lib(qut_pack_t* ppack)
{
	// 更新URL库
	url_update(ppack->url, ntohl(ppack->head.res.time), ppack->head.res.url_tp1
			, ppack->head.res.url_tp2, ppack->head.res.url_tp3, ppack->head.res.rating);
	// 回应请求
	response_clients(ppack->url, ppack->head.res.url_tp1, ppack->head.res.url_tp2
			, ppack->head.res.url_tp3, ppack->head.res.rating);
}
/*
 * 回应缓冲区客户端
 */
void	response_clients(const char* purl, uint8_t tp1, uint8_t tp2
			, uint8_t tp3, uint8_t rating)
{
	struct request_chain*		chain		= root_req_chain;
	struct request_chain*		pre_chain	= NULL;
	unsigned int				timeout		= (unsigned int)time(NULL);
	unsigned int				url_hash	= str_bkdrhash(purl);
	struct request_chain*		temp;

	while( NULL != chain )
	{
		if( url_hash != chain->hash && timeout < chain->timeout )
		{
			pre_chain = chain;
			chain = chain->next;
			continue;
		}
		// 发送下层数据
		if( url_hash == chain->hash )
		{
			sendto_remote(&chain->addr, purl, 1, tp1, tp2, tp3, rating);
		}
		// 从链中断开
		temp = chain->next;
		if( NULL == pre_chain )	
		{
			root_req_chain = chain->next;
		}
		else
		{
			pre_chain->next = chain->next;
		}
		// 释放空间
		free(chain);
		// 指向下一个空间
		chain = temp;
		continue;
	}
}
/*
 * 回应客户端 
 */
void sendto_remote(struct sockaddr_in* paddr, const char* purl
		, uint8_t service, uint8_t tp1, uint8_t tp2
		, uint8_t tp3, uint8_t rating)
{
	char			buff[1024]		= {0};
	qut_pack_t		*ppack			= (qut_pack_t *)buff;
	uint32_t		len				= 9 + strlen(purl);

	// 构造返回数据
	ppack->version = 2;
	ppack->service = service;
	//ppack->des_size = hton(9 + strlen(purl));
	ppack->url_size = (uint8_t)strlen(purl);
	strcpy(ppack->url, purl);
	if( 0 == service )
	{
		ini_get_license(&ppack->head.lcn);
	}
	else
	{
		ppack->head.res.time = htonl((uint32_t)time(NULL) + 60 * 60 * 24 * 30);
		ppack->head.res.url_tp1 = tp1;
		ppack->head.res.url_tp2 = tp2;
		ppack->head.res.url_tp3 = tp3;
		ppack->head.res.rating = rating;
	}
	// 加密数据
	if( false == myencrypt(buff + 4, &len, true) )
	{
		syslog(LOG_ERR, "sendto_remote, encrypt error:%s"
				, strerror(errno)); 
		return;
	}
	ppack->des_size = htons((uint16_t)len);
	// 将数据发送出去
	sendto(fd_sock, buff, 4 + len, 0, (struct sockaddr*)paddr
			, sizeof(struct sockaddr_in));
}
/*
 * 远程请求
 */
void udp_remote_check(struct sockaddr_in* paddr, const char* purl)
{
	struct request_chain*	pchain			= NULL;

	// 分配空间
	pchain = (struct request_chain*)malloc(sizeof(struct request_chain));
	if(NULL == pchain)
	{
		syslog(LOG_ERR, "malloc request_chain failed: %s", strerror(errno));
		return;
	}
	// 存储用户
	pchain->next = root_req_chain;
	root_req_chain = pchain;
	pchain->timeout = (unsigned int)time(NULL) + 60 * 6;
	pchain->hash = str_bkdrhash(purl);
	memcpy(&pchain->addr, paddr, sizeof(struct sockaddr_in));
	// 将数据发送到远程
	sendto_remote(paddr, purl, 0, 0, 0, 0, 0);
}
/*
 * 处理TCP请求
 */
void do_tcp_request(int events, struct tcp_context* ptcp)
{
	if( EPOLLIN == events )
	{
		struct epoll_event		ev		= {0};
		char   data[1024]				= {0};
			
		ev.events = EPOLLIN;
		ev.data.ptr = ptcp;
		epoll_ctl(fd_epoll, EPOLL_CTL_DEL, ptcp->fd, &ev);
		// 接收
		recv(ptcp->fd, data, sizeof(data), 0);
		if(strncasecmp(data, "HTTP/1.1 200 ", 13) == 0)
		{
			char*	ptype	= (char *)strstr(data, "\r\n\r\n");
			int		tp1		= -1;

			if(NULL != ptype)
			{
				tp1 = atoi(ptype+4);
			}
			// 更新URL库
			url_update(ptcp->url, (uint32_t)time(NULL) + 60 * 60 * 24 * 30, tp1
					, 0, 0, 0);
			// 回应请求
			sendto_remote(&ptcp->addr, ptcp->url, 1, tp1, 0, 0, 0);
		}
		// 关闭
		close(ptcp->fd);
		free(ptcp);
	}
	else if( EPOLLOUT == events )
	{
		struct epoll_event		ev		= {0};

		ev.events = EPOLLIN;
		ev.data.ptr = ptcp;
		epoll_ctl(fd_epoll, EPOLL_CTL_MOD, ptcp->fd, &ev);

		tcp_send_get(ptcp);
	}
}
/*
 * TCP远程请求 
 */
void tcp_remote_check(struct sockaddr_in* paddr, const char* purl)
{
	struct tcp_context*			ptcp		= NULL;
	int							async		= 1;
	struct sockaddr_in			addr		= {0};
	char						host[256]	= {0};
	struct hostent*				pent		= NULL;
	int							ret			= 0;

	ini_get_server_host(host);
	pent = gethostbyname(host);
	if(NULL == pent)
	{
		syslog(LOG_ERR, "tcp_remote_check gethostbyname failed: %s(%s)"
				, host, strerror(errno));
		return;
	}
	// 设置上下文
	ptcp = (struct tcp_context*)malloc(sizeof(struct tcp_context));
	if(NULL == ptcp)
	{
		return;
	}
	ptcp->fd = socket(AF_INET, SOCK_STREAM, 0);
	memcpy(&ptcp->addr, paddr, sizeof(struct sockaddr_in));
	strncpy(ptcp->url, purl, 256);
	// 设置套按字为异步
	ioctl(ptcp->fd, FIONBIO, &async);
	// 连接远程主机
	addr.sin_family = AF_INET;
	addr.sin_port = htons(80); // HTTP
	addr.sin_addr.s_addr = *((in_addr_t *)pent->h_addr_list[0]);
	
	ret = connect(ptcp->fd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in));
	if(-1 == ret && EINPROGRESS != errno)
	{
		// 发生错误
		syslog(LOG_ERR, "tcp_remote_check connect %s:80 failed: %s"
				, host, strerror(errno));
		close(ptcp->fd);
		free(ptcp);
		return;
	}
	// 加入监听队列
	if(0 == ret)
	{
		struct epoll_event		ev		= {0};

		ev.events = EPOLLIN;
		ev.data.ptr = ptcp;
		if( epoll_ctl(fd_epoll, EPOLL_CTL_ADD, ptcp->fd, &ev) == -1 )
		{
			syslog(LOG_ERR, "tcp_remote_check epoll_ctl failed: %s"
					, strerror(errno));
			close(ptcp->fd);
			free(ptcp);
			return;
		}
		tcp_send_get(ptcp);
	}
	else
	{
		struct epoll_event		ev		= {0};

		ev.events = EPOLLOUT;
		ev.data.ptr = ptcp;
		if( epoll_ctl(fd_epoll, EPOLL_CTL_ADD, ptcp->fd, &ev) == -1 )
		{
			syslog(LOG_ERR, "tcp_remote_check epoll_ctl failed: %s"
					, strerror(errno));
			close(ptcp->fd);
			free(ptcp);
			return;
		}
	}
}

/*
 * 发送GET请求 
 */
void tcp_send_get(struct tcp_context* ptcp)
{
	char		get[1024]	= {0};
	char		host[256]	= {0};

	ini_get_server_host(host);
	sprintf(get, "GET /ClientResponse.aspx?ReqType=GetUrlType&UrlDomain=%s HTTP/1.1\r\n"
			"Host: %s\r\n\r\n"
			, ptcp->url, host);
	send(ptcp->fd, get, strlen(get)+1, 0);
}


